#pragma once

#include <iostream>
#include <cmd/common.h>

#include "iepe.hpp"
#include "resistance.hpp"
#include "strain_sg.hpp"
#include "thermocouple.hpp"
#include "voltage.hpp"

#define CALLBOARD(sensor,func,x,y,z,c)                  \
    do                                                  \
    {                                                   \
        if(sensor=="WLN-TW-V100"){                      \
            voltage::func(x,y,z,c);                     \
        }else if(sensor=="WLN-TW-RTD100"){              \
            resistance::func(x,y,z,c);                  \
        }else if(sensor=="WLN-TW-TC100"){               \
            thermocouple::func(x,y,z,c);                \
        }else if(sensor=="WLN-TW-SG100"){               \
            strain::func(x,y,z,c);                      \
        }else if(sensor=="WLN-TW-IEPE100"){             \
            iepe::func(x,y,z,c);                        \
        }else{                                          \
            BOOST_LOG_TRIVIAL(info)<<"sensor device not found";       \
        }                                               \
    } while (0)

namespace sensor{
    #pragma pack(1)
    typedef struct __package{
        unsigned int header;
        unsigned int len;
        unsigned char code; 
        unsigned char data[1];
    }package;
    #pragma pack()
    std::vector<unsigned char> make_package(unsigned int len,unsigned char code,std::vector<unsigned char>value){
        package pack_data = { 0x55FF00AA,boost::endian::endian_reverse<unsigned int>(len),code };
        std::vector<unsigned char> data(len+8);
        memcpy(&data[0],&pack_data,OFFSET_OF(package,data));
        if(value.size()>0) memcpy(&data[OFFSET_OF(package,data)],&value[0],value.size());
        return data;
    }

    std::mutex mw,mw_recv,mw_data;
    std::shared_mutex mtx_cache,mtx_buf;
    std::condition_variable cv_recv_flag,cv_data_flag;
    std::map<boost::asio::ip::tcp::socket*,std::vector<unsigned char>> sockets_buf;
    std::map<boost::asio::ip::tcp::socket*,std::tuple<std::string,bool>> sockets;
    std::map<boost::asio::ip::tcp::socket*,std::map<int,std::vector<unsigned char>>> recv_data;
    void recv_thread(std::map<boost::asio::ip::tcp::socket*,std::tuple<std::string,bool>>::iterator socket)
    {
        std::thread([=]{
            boost::system::error_code ec;//错误代码
            if(sockets_buf.find(socket->first)==sockets_buf.end()) sockets_buf[socket->first]=std::vector<unsigned char>();
            while(socket!=sockets.end()){
                std::vector<unsigned char> buf(1024*10,0);//缓冲区
                size_t len = socket->first->read_some(boost::asio::buffer(buf), ec);//接收数据
                if(ec){//接受错误断开连接
                    std::map<boost::asio::ip::tcp::socket*,std::tuple<std::string,bool>>::iterator del_it;
                    BOOST_LOG_TRIVIAL(info) <<"sensor "<< socket->first->remote_endpoint(ec).address().to_string()<<":"<<std::dec
                        <<socket->first->remote_endpoint(ec).port()<<" disconnect";
                    {
                        std::lock_guard<std::mutex> lock(mw);
                        socket->first->close();
                        delete socket->first;
                        del_it = sockets.erase(socket);
                    }
                    return;
                }
                if(len<=0) continue;
                std::unique_lock<std::shared_mutex> lock(mtx_buf);
                append(sockets_buf[socket->first],buf,len);
                cv_recv_flag.notify_one();
#if TEST
                BOOST_LOG_TRIVIAL(info)<<"------------sensor recv "<<len;
#endif
            }
        }).detach();
        std::thread([=]{//发送线程太快，单独的线程解码
            bool b_show=true;
            auto it = socket->first;
            boost::system::error_code ec;
            std::vector<unsigned char> buf;
            while(sockets.find(it)!=sockets.end()){
                size_t len = 0,index=0;
                std::vector<unsigned char> tmp;
                if(buf.size()) buf.swap(tmp);
                {
                    std::unique_lock<std::shared_mutex> lock(mtx_buf);
                    buf.swap(sockets_buf[it]);
                }
                if(tmp.size()) rappend(buf,tmp);
                len = buf.size();
                if(index+8>=len){
                    std::unique_lock<std::mutex> lk(mw_recv);
                    cv_recv_flag.wait(lk);
                }else{
#if TEST
                    BOOST_LOG_TRIVIAL(info)<<"------------sensor process "<<buf.size();
#endif
                }
                while(index+8<len){
                    if (0xAA==buf[index]&&0x00==buf[index+1]&&0xFF==buf[index+2]&&0x55==buf[index+3]) {//帧头判断
                        int pack_len = buf[index+7]|(buf[index+6]<<8)|(buf[index+5]<<16)|(buf[index+4]<<24);
                        if(index+pack_len+8<=len){
                            std::vector<unsigned char> data(buf.begin()+index+9,buf.begin()+(index+9+pack_len-1));
                            {
                                std::unique_lock<std::shared_mutex> lock(mtx_cache);
                                recv_data[it][buf[index+8]]=data.size()==0?std::vector<unsigned char>(1,0):data;
                            }
                            cv_data_flag.notify_one();
                            if(buf[index+8]==0x10 && data.size()>0){
                                data.emplace_back(0);
                                std::vector<std::string> vars;
                                boost::algorithm::split(vars,(char*)&data[0],boost::is_any_of(","));
                                std::get<0>(sensor::sockets.find(it)->second) = vars[0];
                                if(b_show){
                                    b_show=false;
                                    BOOST_LOG_TRIVIAL(info)<<"sensor "<< std::get<0>(sensor::sockets.find(it)->second)<<" "
                                        <<it->remote_endpoint(ec).address().to_string()<<":"<<std::dec<<it->remote_endpoint(ec).port();
                                }
                            }
                            index += pack_len + 8;
                        }else{
                            break;
                        }
                    }else{
                        index++;
                        BOOST_LOG_TRIVIAL(info)<<"sensor "<<std::get<0>(sensor::sockets.find(it)->second)<<" frame header error";
                    }
                }
                if(index >0) {
                    std::vector<unsigned char> left(buf.begin()+index,buf.end());
                    buf.swap(left);
                }
#if TEST
                if(buf.size()) BOOST_LOG_TRIVIAL(info)<<"------------sensor process "<<buf.size();
#endif
            }
            sockets_buf.erase(it);
        }).detach();
    }
    void recv(std::map<boost::asio::ip::tcp::socket*,std::tuple<std::string,bool>>::iterator socket,
        int code,std::function<void(int,const std::vector<unsigned char>&)> callback){
        auto it = socket->first;
        std::vector<unsigned char> data;
        std::string device_name = std::get<0>(sensor::sockets.find(socket->first)->second);
        auto end = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(WAITFOR_DATA);
        while(std::chrono::high_resolution_clock::now()<end){
            {
                std::unique_lock<std::shared_mutex> lock(mtx_cache);
                if(recv_data[it][code].size()){
                    data = recv_data[it][code];
                    recv_data[it][code].clear();
                    break;
                }else if(code<0x50&&recv_data[it][code+1].size()){
                    data = recv_data[it][code+1];
                    recv_data[it][code+1].clear();
                    code+=1;
                    break;
                }else if(code<0x50&&recv_data[it][code+2].size()){
                    data = recv_data[it][code+2];
                    recv_data[it][code+2].clear();
                    code+=2;
                    break;
                }
            }
            std::unique_lock<std::mutex> lk(mw_data);
            cv_data_flag.wait_for(lk,std::chrono::milliseconds(2));
        }
        if (data.size()) {
            callback(code,data);
        }else{
            callback(code,data);
            BOOST_LOG_TRIVIAL(info)<<"sensor "<<device_name<<" code not found "<<std::hex<<code;
        }
    }
    void init(){
        app().registerHandler("/idle", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            if (it != sensor::sockets.end()) {
                boost::system::error_code ignored_error;
                auto pack_data = sensor::make_package(1,0x20,{});
                it->first->write_some(boost::asio::buffer(pack_data),ignored_error);
                sensor::recv(it,0x20,[&](int code,const std::vector<unsigned char>& data){
                    resp->setBody(R"({"state":0})");
                });
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
            if(it!=sensor::sockets.end()){
                std::get<1>(it->second) = false;
            }
        }, { Get })
        .registerHandler("/capture", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            std::string device_name = std::get<0>(sensor::sockets.find(it->first)->second);
            if (it != sensor::sockets.end()) {
                boost::system::error_code ignored_error;
                auto pack_data =sensor::make_package(1,0x30,{});
                unsigned char id = std::distance(sensor::sockets.begin(),it)+SENSOR_ID_BASE;
                it->first->write_some(boost::asio::buffer(pack_data),ignored_error);
                sensor::recv(it,0x30, [&](int code,const std::vector<unsigned char>& data){
                    resp->setBody("{\"state\":"+std::to_string(code==0x31?0:1)+"}");
                    if(!std::get<1>(it->second)){
                        std::get<1>(it->second) = true;
                        boost::asio::post(pool, std::bind([=]{
                            try{
                                std::ofstream outfile;
                                std::map<int,std::vector<double>> cache_data;
                                int save_interval = settings.get<int>("save.interval", 1);
                                settings.put("save.interval",save_interval);
                                int max_size = settings.get<int>("save.max_size", 10);
                                settings.put("save.max_size",max_size);
                                std::string save_path = settings.get<std::string>("save.path", "./raw");
                                settings.put("save.path",save_path);
                                std::filesystem::create_directories(save_path);
                                init_binary_log(save_path+"/capture_"+std::get<0>(it->second)+".bin",save_interval,max_size);
                                auto save_time=std::chrono::high_resolution_clock::now()+std::chrono::seconds(save_interval);
                                auto last_time=std::chrono::high_resolution_clock::now()+std::chrono::milliseconds(WEBSOCKET_INTERVAL);
                                while(std::get<1>(it->second)){
                                    sensor::recv(it,0x50,[&](int code,const std::vector<unsigned char>& data){
                                        for(auto itt:websoket_connection){
                                            if(data.size()&&std::chrono::high_resolution_clock::now()>last_time){
                                                std::vector<unsigned char> send_data;
                                                append(send_data,data2uchar(id));
                                                append(send_data,data);
                                                itt->send((char*)&send_data[0],send_data.size(),WebSocketMessageType::Binary);
                                                if(++std::find(websoket_connection.begin(),websoket_connection.end(),itt)==websoket_connection.end())
                                                    last_time = std::chrono::high_resolution_clock::now()+std::chrono::milliseconds(WEBSOCKET_INTERVAL);
                                            }
                                            
                                        }
                                        //数据校正
                                        try{
                                            //数据保存
                                            if(data.size()) {
                                                BOOST_LOG_TRIVIAL(info)<<std::string((char*)&data[0],data.size());
                                            }
                                        }catch(...){
                                            BOOST_LOG_TRIVIAL(error)<<"feature fail.";
                                        }
                                    });
                                }
                                BOOST_LOG_TRIVIAL(info)<<"sensor "<<device_name<<" task finish.";
                            }catch(...){
                                BOOST_LOG_TRIVIAL(error)<<"sensor "<<device_name<<" task fail.";
                            }
                        }));
                    }
                });
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
        }, { Get })
        .registerHandler("/calibrate", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            if (it != sensor::sockets.end()) {
                boost::system::error_code ignored_error;
                auto pack_data = sensor::make_package(273,0x47,{});
                it->first->write_some(boost::asio::buffer(pack_data),ignored_error);
                sensor::recv(it, 0x47,[&](int code,const std::vector<unsigned char>& data){
                    resp->setBody("{\"state\":"+std::to_string(code==0x48?0:1)+"})");
                });
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
        }, { Get })
        .registerHandler("/set_datetime", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            if (it != sensor::sockets.end()) {
                boost::system::error_code ignored_error;
                auto pack_data = sensor::make_package(7,0x44,{});
                it->first->write_some(boost::asio::buffer(pack_data),ignored_error);
                sensor::recv(it,0x44, [&](int code,const std::vector<unsigned char>& data){
                    resp->setBody(std::string("{\"state\":"+std::to_string(code==0x45?0:1)+"}"));
                });
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
        }, { Get })
        .registerHandler("/import", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            if (it != sensor::sockets.end()) {
                CALLBOARD(std::get<0>(it->second),imports,req,resp,it,[&](int len,int code,std::vector<unsigned char>& data){
                    boost::system::error_code ignored_error;
                    data = sensor::make_package(len,code,data);
                    if(data.size()==len+8){
                        it->first->write_some(boost::asio::buffer(data),ignored_error);
                        sensor::recv(it,code,[&](int code,const std::vector<unsigned char>& data){
                            
                        });
                    }
                });
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
        }, { Post })
        .registerHandler("/export", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            if (it != sensor::sockets.end()) {
                CALLBOARD(std::get<0>(it->second),exports,req,resp,it,nullptr);
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
        }, { Get })
        .registerHandler("/flash_clear", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            if (it != sensor::sockets.end()) {
                boost::system::error_code ignored_error;
                auto pack_data = sensor::make_package(1,0x60,{});
                it->first->write_some(boost::asio::buffer(pack_data),ignored_error);
                sensor::recv(it,0x60, [&](int code,const std::vector<unsigned char>& data){
                    resp->setBody(std::string(R"({"state":0})"));
                });
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
        }, { Get })
        .registerHandler("/flash_clear_used", [&](const HttpRequestPtr& req, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            if(!sensor::sockets.size()){
                callback(resp);
                return;
            }
            auto it = sensor::sockets.begin();
            std::advance(it,atoi(req->getParameter("id").c_str())-SENSOR_ID_BASE);
            if (it != sensor::sockets.end()) {
                boost::system::error_code ignored_error;
                auto pack_data = sensor::make_package(1,0x62,{});
                it->first->write_some(boost::asio::buffer(pack_data),ignored_error);
                sensor::recv(it, 0x62,[&](int code,const std::vector<unsigned char>& data){
                    resp->setBody(std::string(R"({"state":0})"));
                });
            }else{
                resp->setBody((R"({"state":1,"data":"can not found device"})"));
            }
            callback(resp);
        }, { Get });
    }
}